﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Citrix.Cmp;
using Citrix.XaAdminMobile.Model;
using System.Windows.Input;
using Citrix.XaAdminMobile.Util;

namespace Citrix.XaAdminMobile.ViewModel
{
    /// <summary>
    /// The settings page view-model.
    /// </summary>
    internal class SettingsViewModel : INotifyPropertyChanged, IPage
    {
        private const string Localhost = "localhost";

        /// <summary>
        /// A reference back to the root v-m object.
        /// </summary>
        private readonly ViewModelManager vmRoot;

        /// <summary>
        /// The main model object.
        /// </summary>
        private readonly IModelBase model;

        /// <summary>
        /// The set of items we display on the UI for selection. We always maintain a localhost
        /// item at the top of the list and a special "add server item" at the bottom. This item
        /// is used to display the AddServerView so the user can type in a new server name.
        /// </summary>
        private List<object> serverItems = new List<object>();

        /// <summary>
        /// References the item in <see cref="serverItems"/> that is currently selected.
        /// </summary>
        private ServerItemViewModel selectedItem = null;

        /// <summary>
        /// The trigger to display the AddServerView.
        /// </summary>
        private readonly AddServerItemViewModel addServerItem;

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="vmMgr">The root v-m object.</param>
        /// <param name="modelSource">The base model object.</param>
        /// <exception cref="ArgumentNullException">Thrown if any arg is null.</exception>
        public SettingsViewModel(ViewModelManager vmMgr, IModelBase modelSource)
        {
            if (null == vmMgr)
            {
                throw new ArgumentNullException("vmMgr");
            }
            if (null == modelSource)
            {
                throw new ArgumentNullException("modelSource");
            }

            this.vmRoot = vmMgr;
            this.model = modelSource;
            this.addServerItem = new AddServerItemViewModel(new DelegateCommand(() =>
                {
                    this.vmRoot.NavigateToPage(this.vmRoot.AddServerViewModel);
                }));
            SyncWithModel();
        }

        /// <summary>
        /// Called by the AddServerViewModel to notify it to add a new XenApp server to the
        /// list and select it as the active server.
        /// </summary>
        /// <param name="serverName">The server name.</param>
        public void AddNewServer(string serverName)
        {
            if (!string.IsNullOrEmpty(serverName))
            {
                this.vmRoot.UiDispatch(() =>
                {
                    // Look for a matching server name so we don't add the same server twice.
                    ServerItemViewModel serverItemToSelect = null;
                    foreach (var item in this.serverItems)
                    {
                        if (item is ServerItemViewModel)
                        {
                            ServerItemViewModel serverItem = item as ServerItemViewModel;

                            if (string.Equals(serverItem.Name, serverName, StringComparison.OrdinalIgnoreCase))
                            {
                                serverItemToSelect = serverItem;
                                break;
                            }
                        }
                    }

                    //
                    // Create the new server item in the server history.
                    //
                    if (null == serverItemToSelect)
                    {
                        // Should not occur, but for robustness, ensure we always have our special
                        // add server item in the list.
                        if (this.serverItems.Count == 0)
                        {
                            this.serverItems.Add(this.addServerItem);
                        }

                        serverItemToSelect = new ServerItemViewModel(serverName);

                        // Insert as the second last item since we keep the last item as our add
                        // server trigger.
                        this.serverItems.Insert(this.serverItems.Count - 1, serverItemToSelect);

                        // Save the new server history to persistent storage.
                        this.model.ServerConfig.SetServerHistory(ConvertItemsToServerNames());

                        OnPropertyChanged("AvailableServers");
                    }

                    //
                    // Now select the server item. This triggers a save in the model layer as
                    // well as updating the UI.
                    //
                    this.SelectedItem = serverItemToSelect;
                });
            }
        }

        /// <summary>
        /// Returns the set of items to display in the "Choose a server" list. This contains
        /// <see cref="ServerItemViewModel"/> instances for all the servers you can choose. The
        /// top item is always a localhost instance. To allow the user to add a new server, the
        /// bottom item is always an <see cref="AddServerItemViewModel"/> instance. Tapping on
        /// this item displays the add server view that allows the user to add a new server.
        /// </summary>
        public List<object> AvailableServers
        {
            get
            {
                // Unless using an observable collection we must generate a new container to
                // make WPF pick up added/removed items.
                return new List<object>(this.serverItems);
            }
        }

        /// <summary>
        /// The master navigation toolbar v-m.
        /// </summary>
        public NavToolbarViewModel NavToolbarViewModel
        {
            get { return this.vmRoot.NavToolbarViewModel; }
        }

        /// <summary>
        /// Bound to the selected item
        /// </summary>
        public object SelectedItem
        {
            get
            {
                return this.selectedItem;
            }
            set
            {
                if (!object.ReferenceEquals(value, this.selectedItem))
                {
                    if (value is ServerItemViewModel)
                    {
                        ServerItemViewModel typedVal = value as ServerItemViewModel;
                        this.selectedItem = typedVal;
                        OnPropertyChanged("SelectedItem");

                        // Check if the new selection differs from the current server name in
                        // the model layer.
                        if (!string.Equals(this.model.ServerConfig.ServerName, typedVal.Name, StringComparison.OrdinalIgnoreCase))
                        {
                            this.model.ServerConfig.SetServerName(typedVal.Name);

                            //
                            // On selecting a new server we refresh all of our state.
                            //
                            this.model.Servers.Refresh();
                            this.model.Sessions.Refresh();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Converts our list of server name v-m objects into a string list of server names.
        /// </summary>
        /// <returns>String list of server names.</returns>
        private List<string> ConvertItemsToServerNames()
        {
            List<string> result = new List<string>();

            foreach (var s in this.serverItems)
            {
                // Only grab the server name items.
                if (s is ServerItemViewModel)
                {
                    result.Add(((ServerItemViewModel)s).Name);
                }
            }

            return result;
        }

        /// <summary>
        /// Loads all the model state and reflects in the v-m for display to the user.
        /// </summary>
        private void SyncWithModel()
        {
            this.vmRoot.AssertUiThread();

            var serverHistory = this.model.ServerConfig.ServerHistory;
            this.serverItems.Clear();

            // Setup localhost default.
            ServerItemViewModel itemToSelect = new ServerItemViewModel(Localhost);
            this.serverItems.Add(itemToSelect);

            // Copy in the server history.
            foreach (var s in serverHistory)
            {
                var serverItem = new ServerItemViewModel(s);
                if (string.Equals(this.model.ServerConfig.ServerName, s, StringComparison.OrdinalIgnoreCase))
                {
                    itemToSelect = serverItem;
                }
                this.serverItems.Add(serverItem);
            }

            this.serverItems.Add(this.addServerItem);

            // Now ensure the currently configured server is displayed as selected.
            this.SelectedItem = itemToSelect;

            OnPropertyChanged("AvailableServers");
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Fired whenever a public property changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property change event.
        /// </summary>
        /// <param name="propertyName">The name of the property that changed.</param>
        private void OnPropertyChanged(string propertyName)
        {
            NotifyHelper.VerifyProperty(propertyName, this);
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion // INotifyPropertyChanged Members

        #region IPage Members

        /// <summary>
        /// Used to notify the page when is made the active page, or when it has become inactive.
        /// </summary>
        /// <param name="isActive">True if this page is now active, false if it has become
        /// inactive.</param>
        public void NotifyActiveChanged(bool isActive)
        {
        }

        /// <summary>
        /// Notifies the active page that the physical back button has been pressed on the
        /// client device.
        /// </summary>
        public void NotifyBackButtonPressed()
        {
        }

        /// <summary>
        /// Used to access the back command that will navigate you from the current page to the
        /// previous page in the navigation history.
        /// </summary>
        public ViewModelManager VmManager
        {
            get { return this.vmRoot; }
        }

        #endregion
    }
}
